#include "qhotkey.h"
#include "qhotkey_p.h"
#include <qt_windows.h>
#include <QDebug>

#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)

class QHotkeyPrivateWin : public QHotkeyPrivate
{
public:
	// QAbstractNativeEventFilter interface
	bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;

protected:
	// QHotkeyPrivate interface
	quint32 nativeKeycode(Qt::Key keycode) Q_DECL_OVERRIDE;
	quint32 nativeModifiers(Qt::KeyboardModifiers modifiers) Q_DECL_OVERRIDE;
	bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
	bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;

private:
	static QString formatWinError(DWORD winError);
};
NATIVE_INSTANCE(QHotkeyPrivateWin)

bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
	Q_UNUSED(eventType);
	Q_UNUSED(result);

	MSG* msg = static_cast<MSG*>(message);
	if(msg->message == WM_HOTKEY)
		this->activateShortcut({HIWORD(msg->lParam), LOWORD(msg->lParam)});

	return false;
}

quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode)
{
	if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character"
		const SHORT vKey = VkKeyScanW(keycode);
		if(vKey > -1)
			return LOBYTE(vKey);
	}

	//find key from switch/case --> Only finds a very small subset of keys
	switch (keycode)
	{
	case Qt::Key_Escape:
		return VK_ESCAPE;
	case Qt::Key_Tab:
	case Qt::Key_Backtab:
		return VK_TAB;
	case Qt::Key_Backspace:
		return VK_BACK;
	case Qt::Key_Return:
	case Qt::Key_Enter:
		return VK_RETURN;
	case Qt::Key_Insert:
		return VK_INSERT;
	case Qt::Key_Delete:
		return VK_DELETE;
	case Qt::Key_Pause:
		return VK_PAUSE;
	case Qt::Key_Print:
		return VK_PRINT;
	case Qt::Key_Clear:
		return VK_CLEAR;
	case Qt::Key_Home:
		return VK_HOME;
	case Qt::Key_End:
		return VK_END;
	case Qt::Key_Left:
		return VK_LEFT;
	case Qt::Key_Up:
		return VK_UP;
	case Qt::Key_Right:
		return VK_RIGHT;
	case Qt::Key_Down:
		return VK_DOWN;
	case Qt::Key_PageUp:
		return VK_PRIOR;
	case Qt::Key_PageDown:
		return VK_NEXT;
	case Qt::Key_CapsLock:
		return VK_CAPITAL;
	case Qt::Key_NumLock:
		return VK_NUMLOCK;
	case Qt::Key_ScrollLock:
		return VK_SCROLL;

	case Qt::Key_F1:
		return VK_F1;
	case Qt::Key_F2:
		return VK_F2;
	case Qt::Key_F3:
		return VK_F3;
	case Qt::Key_F4:
		return VK_F4;
	case Qt::Key_F5:
		return VK_F5;
	case Qt::Key_F6:
		return VK_F6;
	case Qt::Key_F7:
		return VK_F7;
	case Qt::Key_F8:
		return VK_F8;
	case Qt::Key_F9:
		return VK_F9;
	case Qt::Key_F10:
		return VK_F10;
	case Qt::Key_F11:
		return VK_F11;
	case Qt::Key_F12:
		return VK_F12;
	case Qt::Key_F13:
		return VK_F13;
	case Qt::Key_F14:
		return VK_F14;
	case Qt::Key_F15:
		return VK_F15;
	case Qt::Key_F16:
		return VK_F16;
	case Qt::Key_F17:
		return VK_F17;
	case Qt::Key_F18:
		return VK_F18;
	case Qt::Key_F19:
		return VK_F19;
	case Qt::Key_F20:
		return VK_F20;
	case Qt::Key_F21:
		return VK_F21;
	case Qt::Key_F22:
		return VK_F22;
	case Qt::Key_F23:
		return VK_F23;
	case Qt::Key_F24:
		return VK_F24;

	case Qt::Key_Menu:
		return VK_APPS;
	case Qt::Key_Help:
		return VK_HELP;
	case Qt::Key_MediaNext:
		return VK_MEDIA_NEXT_TRACK;
	case Qt::Key_MediaPrevious:
		return VK_MEDIA_PREV_TRACK;
	case Qt::Key_MediaPlay:
		return VK_MEDIA_PLAY_PAUSE;
	case Qt::Key_MediaStop:
		return VK_MEDIA_STOP;
	case Qt::Key_VolumeDown:
		return VK_VOLUME_DOWN;
	case Qt::Key_VolumeUp:
		return VK_VOLUME_UP;
	case Qt::Key_VolumeMute:
		return VK_VOLUME_MUTE;
	case Qt::Key_Mode_switch:
		return VK_MODECHANGE;
	case Qt::Key_Select:
		return VK_SELECT;
	case Qt::Key_Printer:
		return VK_PRINT;
	case Qt::Key_Execute:
		return VK_EXECUTE;
	case Qt::Key_Sleep:
		return VK_SLEEP;
	case Qt::Key_Period:
		return VK_DECIMAL;
	case Qt::Key_Play:
		return VK_PLAY;
	case Qt::Key_Cancel:
		return VK_CANCEL;

	case Qt::Key_Forward:
		return VK_BROWSER_FORWARD;
	case Qt::Key_Refresh:
		return VK_BROWSER_REFRESH;
	case Qt::Key_Stop:
		return VK_BROWSER_STOP;
	case Qt::Key_Search:
		return VK_BROWSER_SEARCH;
	case Qt::Key_Favorites:
		return VK_BROWSER_FAVORITES;
	case Qt::Key_HomePage:
		return VK_BROWSER_HOME;

	case Qt::Key_LaunchMail:
		return VK_LAUNCH_MAIL;
	case Qt::Key_LaunchMedia:
		return VK_LAUNCH_MEDIA_SELECT;
	case Qt::Key_Launch0:
		return VK_LAUNCH_APP1;
	case Qt::Key_Launch1:
		return VK_LAUNCH_APP2;

	case Qt::Key_Massyo:
		return VK_OEM_FJ_MASSHOU;
	case Qt::Key_Touroku:
		return VK_OEM_FJ_TOUROKU;

	default:
		return 0;
	}
}

quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
	quint32 nMods = 0;
	if (modifiers & Qt::ShiftModifier)
		nMods |= MOD_SHIFT;
	if (modifiers & Qt::ControlModifier)
		nMods |= MOD_CONTROL;
	if (modifiers & Qt::AltModifier)
		nMods |= MOD_ALT;
	if (modifiers & Qt::MetaModifier)
		nMods |= MOD_WIN;
	return nMods;
}

bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut)
{
	BOOL ok = RegisterHotKey(NULL,
							 HKEY_ID(shortcut),
							 shortcut.modifier,
							 shortcut.key);
	if(ok)
		return true;
	else {
		qCWarning(logQHotkey) << "Failed to register hotkey. Error:"
							  << qPrintable(QHotkeyPrivateWin::formatWinError(::GetLastError()));
		return false;
	}
}

bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut)
{
	BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut));
	if(ok)
		return true;
	else {
		qCWarning(logQHotkey) << "Failed to unregister hotkey. Error:"
							  << qPrintable(QHotkeyPrivateWin::formatWinError(::GetLastError()));
		return false;
	}
}

QString QHotkeyPrivateWin::formatWinError(DWORD winError)
{
	wchar_t *buffer = NULL;
	DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
							   NULL,
							   winError,
							   0,
							   (LPWSTR)&buffer,
							   0,
							   NULL);
	if(buffer) {
		QString res = QString::fromWCharArray(buffer, num);
		LocalFree(buffer);
		return res;
	} else
		return QString();
}
